import { apiFetchFileBlob, apiFetchFileBlobByUrl } from '@/services/file.server';
import { Base64, BlobUrl, ImagePosModel, ImageSizeModel, ImgSrc } from '@/types/model/image.model';
import html2canvas from 'html2canvas';

interface MergeImageConfig extends ImagePosModel {
  opacity?: number;
}

/**
 * 合并图片源码
 * @param sources
 * @param options
 */
export const mergeImages = (
  sources: MergeImageConfig[],
  options: {
    crossOrigin?: HTMLImageElement['crossOrigin'];
    format?: string; // 图片质量
    quality?: number; // 图片格式
    width?: number;
    height?: number;
  },
) =>
  new Promise((resolve) => {
    const { crossOrigin = 'Anonymous', format = 'image/jpeg', quality = 1 } = options;

    const canvas = window.document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    const img = new Image();

    // Load sources
    const images: Promise<
      {
        img: HTMLImageElement;
      } & MergeImageConfig
    >[] = sources.map(
      (source) =>
        new Promise((res, rej) => {
          img.crossOrigin = crossOrigin;
          img.onerror = () => rej(new Error("Couldn't load image"));
          img.onload = () => {
            res(Object.assign({}, source, { img }));
          };
          img.src = source.src;
        }),
    );

    resolve(
      Promise.all(images).then((imgs) => {
        // Set canvas dimensions
        const getSize = (dim) => options[dim] || Math.max(...imgs.map((image) => image.img[dim]));
        canvas.width = getSize('width');
        canvas.height = getSize('height');

        // Draw images to canvas
        imgs.forEach((image) => {
          ctx.globalAlpha = image.opacity ? image.opacity : 1;
          return ctx.drawImage(image.img, image.x || 0, image.y || 0);
        });

        // Resolve all other data URIs sync
        return canvas.toDataURL(format, quality);
      }),
    );
  });

/**
 * 通过id获取图片的Blob文件流
 * @param id
 */
export function getImageBlobById(id: string): Promise<Blob> {
  return apiFetchFileBlob(id);
}

/**
 * 通过url获取 图片的Blob文件流
 * @param url
 */
export function getImageBlobByUrl(url: string): Promise<Blob> {
  return apiFetchFileBlobByUrl(url);
}

/**
 * Blob转Base64
 * @param blob
 */
export async function blobToBase64(blob: Blob): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve(reader.result as string);
    reader.onerror = reject;
    reader.readAsDataURL(blob);
  });
}

/**
 * base64转Blob
 * @param base64String
 * @param fileType
 * @param blobProperty
 */
export function base64ToBlob(
  base64String: Base64,
  fileType: string = 'image/png',
  blobProperty?: BlobPropertyBag,
) {
  const binaryString = atob(base64String.split(',')[1]); // 解码 Base64 字符串
  const len = binaryString.length;
  const bytes = new Uint8Array(len);
  for (let i = 0; i < len; i++) {
    bytes[i] = binaryString.charCodeAt(i);
  }
  // 创建 Blob 对象
  return new Blob([bytes], { type: fileType, ...blobProperty });
}

/**
 * blob=》blobUrl 从后端获取的文件流想通过url的形式展示在img标签中
 * @param blob Blob文件流或 File文件
 */
export function blobToUrl(blob: Blob | File): string {
  return URL.createObjectURL(blob);
}

/**
 * 回收url连接的内存资源
 * @param url
 */
export function revokeBlobUrl(url: BlobUrl) {
  URL.revokeObjectURL(url);
}

/**
 * Blob=>File 将分散的文件流合并，并添添加额外的文件的相关信息
 * @param blob
 * @param fileName
 * @param fileType
 * @param fileProperty
 */
export function blobToFile(
  blob: Blob,
  fileName: string,
  fileType?: string,
  fileProperty?: FilePropertyBag,
) {
  return new File([blob], fileName, {
    type: fileType,
    lastModified: Date.now(),
    ...fileProperty,
  });
}

/**
 * File转为Blob，对 File 对象进行分片操作
 * @param file
 */
export function fileToBlob(file: File): Blob {
  const fileStream = file.slice();
  //对 File 对象进行分片操作
  return fileStream;
}

/**
 * 获取图片的宽高，通过img标签
 * @param src
 */
export function getImageSize(src: ImgSrc): Promise<ImageSizeModel> {
  return new Promise((resolve, reject) => {
    const img = document.createElement('img');
    img.src = src;
    img.crossOrigin = 'Anonymous';
    img.onload = () => {
      resolve({ width: img.naturalWidth, height: img.naturalHeight, src });
    };
    img.onerror = (err) => {
      reject(err);
    };
  });
}

/**
 * 改变图片宽高，宽高比不对应会变形，不会缺失
 * @param src
 * @param width
 * @param height
 */
export function changeImageSize(src: ImgSrc, width: number, height: number): Promise<Base64> {
  return new Promise((resolve, reject) => {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    canvas.width = width; //画布宽
    canvas.height = height; //画布高
    context.rect(0, 0, canvas.width, canvas.height);
    const img = new Image();
    img.src = src;
    img.crossOrigin = 'Anonymous';
    img.onload = () => {
      context.drawImage(img, 0, 0, width, height);
      const base64 = canvas.toDataURL('image/png');
      resolve(base64);
    };
    img.onerror = (err) => {
      reject(err);
    };
  });
}

/**
 * 改变图片宽高，宽高比不对应会缺失
 * @param src
 * @param width
 * @param height
 * @param direction 对齐方向，宽高比不等时从哪里对齐
 */
export function changeImageSizeHard(
  src: ImgSrc,
  width: number,
  height: number,
  direction: 'left' | 'end' | 'center' = 'center',
): Promise<Base64> {
  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d');
  const img = new Image();
  img.src = src;
  img.crossOrigin = 'Anonymous';
  const getOffset = (num, targetNum) => {
    switch (direction) {
      case 'center':
        return Math.round((num - targetNum) / 2);
      case 'left':
        return 0;
      case 'end':
        return num - targetNum;
      default:
        return;
    }
  };
  return new Promise((resolve, reject) => {
    img.onload = () => {
      const aspectRatio = img.width / img.height;
      const targetAspectRatio = width / height;
      let offsetX = 0;
      let offsetY = 0;
      let targetWidth;
      let targetHeight;

      if (aspectRatio > targetAspectRatio) {
        targetHeight = height;
        targetWidth = Math.round(targetHeight * aspectRatio);
        offsetX = getOffset(width, targetWidth);
      } else if (aspectRatio < targetAspectRatio) {
        targetWidth = width;
        targetHeight = Math.round(targetWidth / aspectRatio);
        offsetY = getOffset(height, targetHeight);
      } else {
        targetWidth = width;
        targetHeight = height;
      }
      canvas.width = width;
      canvas.height = height;
      context.drawImage(img, offsetX, offsetY, targetWidth, targetHeight);
      const base64 = canvas.toDataURL('image/png');
      resolve(base64);
    };
    img.onerror = () => reject(new Error('无法加载图像'));
  });
}

/**
 * 合并图片
 * @param images
 * @param width
 * @param height
 */
export async function getMergeImages(
  images: ImagePosModel[],
  width: number,
  height: number,
): Promise<Base64> {
  if (!images?.length) {
    throw new Error('没有需要合并的图片');
  }
  if (images.length === 1) {
    return images[0].src;
  }
  const mergeConfig = images.map((item) => ({
    src: item.src,
    x: item.x,
    y: item.y,
  }));

  // @ts-ignore
  return await mergeImages(mergeConfig, { width, height });
}

/**
 * html转为base64图片格式
 */
export function getHtmlImage(el: HTMLElement): Promise<Base64> {
  return html2canvas(el, {
    width: el.clientWidth, //画布的宽
    height: el.clientHeight, //画布的高
    scale: 1, //处理模糊问题
    useCORS: true, //开启跨域，这个是必须的
  }).then((canvas) => {
    return canvas.toDataURL('image/png');
  });
}

/**
 * 在图片中写文本
 * @param imgSrc
 * @param textPosInfos
 * @param options
 */
type TextCtxConfig = Partial<Pick<CanvasRenderingContext2D, 'font' | 'fillStyle'>>; //字体大小，文本颜色
type TextPosInfo = {
  text: string;
  x: number;
  y: number;
} & TextCtxConfig;

export function writeTextInImg(
  imgSrc: ImgSrc,
  textPosInfos: TextPosInfo[] = [],
  options: TextCtxConfig = {},
): Promise<Base64> {
  const { font = '700 220px SimSun', fillStyle = '#ff616f' } = options;
  const textConfig = { fillStyle, font };
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  const img = new Image();
  img.crossOrigin = 'Anonymous';
  img.src = imgSrc;
  return new Promise((resolve, reject) => {
    img.onload = () => {
      canvas.width = img.width;
      canvas.height = img.height;
      //画布和图片上的距离（戳记和图片是一体的）
      ctx.drawImage(img, 0, 0);
      // 设置文本的垂直对齐方式
      ctx.textBaseline = 'top';
      ctx.textAlign = 'left';
      textPosInfos.forEach((textPos) => {
        Object.entries(textConfig).forEach(([item, val]) => {
          ctx[item] = textPos[item] || val;
        });
        ctx.fillText(textPos.text, textPos.x, textPos.y);
      });
      const base64 = canvas.toDataURL('image/png');
      resolve(base64);
    };
    img.onerror = () => {
      reject(new Error('无法加载图像'));
    };
  });
}
